#include "sequencial.h"
#include "gerenciaMatriz.h"
#include "gerenciaThreads.h"
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/time.h>

extern int NTHREADS;
extern int dim;
extern int quadrante;
extern FILE *entrada;


int main(int argc, char *argv[]) {
  int **mat1;
  char arquivo[20];
  int programa;
  
  int max; 
  int min; 
  int sum;
  
  int i; // variaval auxiliar do loop de criacao das threads
 
  printf("Entre com o arquivo de matriz a ser reduzida:\n");
   
  scanf("%s", arquivo);
  entrada = fopen (arquivo, "r");
  if (entrada == NULL) {
      printf ("\nNão encontrei arquivo\n");
      exit (EXIT_FAILURE);
  }
  
  printf("Qual implementação quer rodar? \n\n -> Sequencial(Digite 1) \n\n -> Concorrente com divisao dinamica  (Digite 2)\n\n ->Concorrente com quatro threads fixas (Digite 3)\n\n");
  scanf("%d", &programa);
  printf("\n\n - - - - - - - - - - - - - - - - - - - - - - - - \n\n");

  struct timeval startTime, endTime;
  unsigned int totalUsecs;

      //guarda a dimensao da matriz
      fscanf(entrada, "%d", &dim);

      //--carrega as matrizes de entrada
      mat1 = carregaMatriz(dim);



  switch(programa){
	case(1): // roda aplicacao sequencial

	//--inicia o contador de tempo
	gettimeofday(&startTime, NULL);

	max = max_matriz(mat1, dim);
	min = min_matriz(mat1, dim);
	sum = sum_matriz(mat1, dim);

	gettimeofday(&endTime, NULL);
	totalUsecs = (unsigned long long) (endTime.tv_sec - startTime.tv_sec) * 1000000 +
		    (unsigned long long) (endTime.tv_usec - startTime.tv_usec);


	printf("Menor %d\n", min);
	printf("Maior %d\n", max);
	printf("Soma %d\n", sum);

	printf("%s%g\n", "Tempo Sequencial = ", (double) totalUsecs);
	break;
    
	case(2): // roda aplicacao concorrente com divisao da matriz fixa
	    quadrante = 8; // dimensao do quadrante trabalhado pela thread
	    
	    
	      calculaNTHREADS(dim);

	      gettimeofday(&startTime, NULL);


	      for(i = 0; i < NTHREADS; i++){
		if(iniciaThread(i, mat1)) {
		  printf("ERRO-- Número de Threads criado é muito alto.\nEntre com uma dimensão melhor: ");
		  scanf("%d", &quadrante);
		  calculaNTHREADS(dim); //reformula o calculo e reinicia
		  pthread_exit(NULL);
		  i=0;
		}
		
	      }

	      for( i = 0; i < NTHREADS; i++){
		if(esperaThreadsTerminarem(i)) {printf("ERRO-- falha esperaThreadsTerminarems()\n");exit(-1);}
	      }

	      gettimeofday(&endTime, NULL);
	      totalUsecs = (unsigned long long) (endTime.tv_sec - startTime.tv_sec) * 1000000 +
			  (unsigned long long) (endTime.tv_usec - startTime.tv_usec);
	      
			  
	      printf("%s%g\n", "Tempo Execucao Concorrente = ", (double) totalUsecs);
	      
	      break;  
	    
	case(3): // roda aplicacao concorrente com divisao da matriz fixa
	  quadrante = dim/2; // dimensao do quadrante trabalhado pela thread
	  
	  
	  calculaNTHREADS(dim);

	  gettimeofday(&startTime, NULL);


	  for(i = 0; i < NTHREADS; i++){
	      if(iniciaThread(i, mat1)) {
		printf("ERRO-- Número de Threads criado é muito alto.\nEntre com uma dimensão melhor: ");
		scanf("%d", &quadrante);
		calculaNTHREADS(dim); //reformula o calculo e reinicia
		pthread_exit(NULL);
		i=0;
	      }
	  }

	    for( i = 0; i < NTHREADS; i++){
	      if(esperaThreadsTerminarem(i)) {printf("ERRO-- falha esperaThreadsTerminarems()\n");exit(-1);}
	    }

	    gettimeofday(&endTime, NULL);
	    totalUsecs = (unsigned long long) (endTime.tv_sec - startTime.tv_sec) * 1000000 +
			(unsigned long long) (endTime.tv_usec - startTime.tv_usec);
	    
			
	    printf("%s%g\n", "Tempo Execucao Concorrente = ", (double) totalUsecs);
	    
	    break;  
	    } 
	  
    printf("\n - - - - - - - - - - - - - - - - - - - - - - - - \n\n");
    return 0;
  }

